package com.imooc.imoocbilibili.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.imooc.imoocbilibili.constants.AuthRoleConstant;
import com.imooc.imoocbilibili.constants.UserConstant;
import com.imooc.imoocbilibili.entity.*;
import com.imooc.imoocbilibili.enums.ErrorResultEnum;
import com.imooc.imoocbilibili.exception.RRException;
import com.imooc.imoocbilibili.interceptor.ThreadUser;
import com.imooc.imoocbilibili.service.*;
import com.imooc.imoocbilibili.utils.MD5Util;
import com.imooc.imoocbilibili.utils.RSAUtil;
import com.imooc.imoocbilibili.utils.TokenUtil;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import com.imooc.imoocbilibili.mapper.UserMapper;
import org.springframework.transaction.annotation.Transactional;

import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Service("userService")
@RequiredArgsConstructor(onConstructor_ = {@Lazy})
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserInfoService userInfoService;
    private final UserFollowingService userFollowingService;
    private final UserRoleService userRoleService;
    private final AuthRoleService authRoleService;
    private final RefreshTokenService refreshTokenService;

    @Override
    public Page<UserInfo> queryPage(Integer pageNo, Integer pageSize, String nick) {
        Long userId = ThreadUser.getUser().getId();
        Page<UserInfo> page = userInfoService.page(
                new Page<>(pageNo, pageSize),
                // 拼接查询条件
                new LambdaQueryWrapper<UserInfo>().eq(StrUtil.isNotBlank(nick), UserInfo::getNick, nick).orderByDesc(UserInfo::getCreatetime)
        );

        // 判断当前用户是否关注过查询出来的用户
        if (CollectionUtil.isNotEmpty(page.getRecords())) {
            List<UserInfo> userInfoList = page.getRecords();
            for (UserInfo userInfo : userInfoList) {
                long count = userFollowingService.count(new LambdaQueryWrapper<UserFollowing>()
                        .eq(UserFollowing::getUserid, userId).eq(UserFollowing::getFollowingid, userInfo.getUserid()));
                userInfo.setFollowed(count > 0);
            }
        }

        return page;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addUser(User user) {
        String phone = user.getPhone();
        if (StrUtil.isBlank(phone)) {
            throw new RRException("手机号不能为空！");
        }
        User dbUser = this.baseMapper.selectOne(new LambdaQueryWrapper<User>().eq(User::getPhone, phone));
        if (dbUser != null) {
            throw new RRException("该手机号已经注册！");
        }
        Date now = new Date();
        String salt = String.valueOf(now.getTime());
        String password = user.getPassword();
        String rawPassword;
        try {
            // 加密的密码可以通过RSAUtil生成
            rawPassword = RSAUtil.decrypt(password);
        } catch (Exception e) {
            throw new RRException("密码解密失败！");
        }
        String md5Password = MD5Util.sign(rawPassword, salt, "UTF-8");
        user.setSalt(salt);
        user.setPassword(md5Password);
        user.setCreatetime(now);
        this.save(user);

        //添加用户信息
        UserInfo userInfo = new UserInfo();
        userInfo.setUserid(user.getId());
        userInfo.setNick(UserConstant.DEFAULT_NICK);
        userInfo.setBirth(UserConstant.DEFAULT_BIRTH);
        userInfo.setGender(UserConstant.GENDER_MALE);
        userInfo.setCreatetime(now);
        userInfoService.save(userInfo);
        //添加用户默认权限角色
        UserRole userRole = new UserRole();
        userRole.setUserid(user.getId());
        AuthRole authRole = authRoleService.getOne(new LambdaQueryWrapper<AuthRole>().eq(AuthRole::getCode, AuthRoleConstant.ROLE_LV0));
        userRole.setRoleid(authRole.getId());
        userRoleService.save(userRole);
    }

    @Override
    public String login(User user) throws Exception {
        String phone = user.getPhone() == null ? "" : user.getPhone();
        String email = user.getEmail() == null ? "" : user.getEmail();
        if (StrUtil.isBlank(phone) && StrUtil.isBlank(email)) {
            throw new RRException(ErrorResultEnum.PARAM_ERROR);
        }
        User dbUser = baseMapper.selectOne(new LambdaQueryWrapper<User>()
                .and(obj->{
                    obj.eq(StrUtil.isNotBlank(phone), User::getPhone, phone)
                            .or()
                            .eq(StrUtil.isNotBlank(email), User::getEmail, email);
                })
        );
        if (dbUser == null) {
            throw new RRException("当前用户不存在！");
        }
        String password = user.getPassword();
        String rawPassword;
        try {
            rawPassword = RSAUtil.decrypt(password);
        } catch (Exception e) {
            throw new RRException("密码解密失败！");
        }
        String salt = dbUser.getSalt();
        String md5Password = MD5Util.sign(rawPassword, salt, "UTF-8");
        if (!md5Password.equals(dbUser.getPassword())) {
            throw new RRException("密码错误！");
        }
        return TokenUtil.generateToken(dbUser.getId());

    }

    @Override
    public Map<String, Object> loginForDts(User user) throws Exception{
        String phone = user.getPhone() == null ? "" : user.getPhone();
        String email = user.getEmail() == null ? "" : user.getEmail();
        if (StrUtil.isBlank(phone) && StrUtil.isBlank(email)) {
            throw new RRException(ErrorResultEnum.PARAM_ERROR);
        }
        User dbUser = baseMapper.selectOne(new LambdaQueryWrapper<User>()
                .and(obj->{
                    obj.eq(StrUtil.isNotBlank(phone), User::getPhone, phone)
                            .or()
                            .eq(StrUtil.isNotBlank(email), User::getEmail, email);
                })
        );
        if (dbUser == null) {
            throw new RRException("当前用户不存在！");
        }
        String password = user.getPassword();
        String rawPassword;
        try {
            rawPassword = RSAUtil.decrypt(password);
        } catch (Exception e) {
            throw new RRException("密码解密失败！");
        }
        String salt = dbUser.getSalt();
        String md5Password = MD5Util.sign(rawPassword, salt, "UTF-8");
        if (!md5Password.equals(dbUser.getPassword())) {
            throw new RRException("密码错误！");
        }

        Long userId = dbUser.getId();
        String accessToken = TokenUtil.generateToken(userId);
        String refreshToken = TokenUtil.generateRefreshToken(userId);
        // 保存refreshToken到数据库
        refreshTokenService.remove(new LambdaQueryWrapper<RefreshToken>()
        .eq(RefreshToken::getUserid, userId));
        RefreshToken refreshTokenObj = new RefreshToken();
        refreshTokenObj.setUserid(userId);
        refreshTokenObj.setRefreshtoken(refreshToken);
        refreshTokenObj.setCreatetime(new Date());
        refreshTokenService.save(refreshTokenObj);

        Map<String, Object> map = new HashMap<>();
        map.put("accessToken", accessToken);
        map.put("refreshToken", refreshToken);
        return map;
    }

    @Override
    public void logout() {
        Long userId = ThreadUser.getUser().getId();
        refreshTokenService.remove(new LambdaQueryWrapper<RefreshToken>()
                .eq(RefreshToken::getUserid, userId));
        ThreadUser.clear();
    }

    @Override
    public Map<String, Object> refreshAccessTokens(String refreshToken) throws Exception {
        RefreshToken refreshTokenServiceDb = refreshTokenService.getOne(new LambdaQueryWrapper<RefreshToken>()
                .eq(RefreshToken::getRefreshtoken, refreshToken));

        if (refreshTokenServiceDb == null) {
            throw new RRException(ErrorResultEnum.INVALID_TOKEN);
        }

        Long userId = TokenUtil.verifyToken(refreshTokenServiceDb.getRefreshtoken());
        if (!userId.equals(ThreadUser.getUser().getId())) {
            throw new RRException(ErrorResultEnum.INVALID_TOKEN);
        }

        String accessToken = TokenUtil.generateToken(userId);
        String refreshTokenNew = TokenUtil.generateRefreshToken(userId);
        // 保存refreshToken到数据库
        refreshTokenService.remove(new LambdaQueryWrapper<RefreshToken>()
                .eq(RefreshToken::getUserid, userId));
        RefreshToken refreshTokenObj = new RefreshToken();
        refreshTokenObj.setUserid(userId);
        refreshTokenObj.setRefreshtoken(refreshTokenNew);
        refreshTokenObj.setCreatetime(new Date());
        refreshTokenService.save(refreshTokenObj);

        Map<String, Object> map = new HashMap<>();
        map.put("accessToken", accessToken);
        map.put("refreshToken", refreshTokenNew);
        return map;
    }

}